package com.xindacloud.sms.adapter.service.sms.impl;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.google.gson.Gson;
import com.xindacloud.sms.adapter.bo.*;
import com.xindacloud.sms.adapter.entity.SmsSubmissionRecord;
import com.xindacloud.sms.adapter.entity.SmsUserAllowOperator;
import com.xindacloud.sms.adapter.exception.ServiceException;
import com.xindacloud.sms.adapter.service.business.ISmsSubmissionRecordService;
import com.xindacloud.sms.adapter.service.business.ISmsUserAllowOperatorService;
import com.xindacloud.sms.adapter.service.business.ISmsUserService;
import com.xindacloud.sms.adapter.service.sms.ISubmitMessageService;
import com.xindacloud.sms.adapter.utils.*;
import com.xindacloud.sms.adapter.vo.SubmitMessageResultVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.HmacAlgorithms;
import org.apache.commons.codec.digest.HmacUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

@Service("domesticSubmitMessageService")
@Slf4j
public class DomesticSubmitMessageServiceImpl implements ISubmitMessageService<SubmitMessageBO, SubmitMessageResultVO> {

    @Autowired
    private ISmsUserAllowOperatorService smsUserAllowOperatorService;
    @Autowired
    private ISmsSubmissionRecordService smsSubmissionRecordService;
    @Autowired
    private ISmsUserService smsUserService;

    @Override
    public SubmitMessageResultVO submitMessage(SubmitMessageBO submitMessageBO) {

        final String apiKey = submitMessageBO.getApiKey();
        final String apiSecret = submitMessageBO.getApiSecret();
        final String apiUrl = submitMessageBO.getApiUrl();
        final String callBackUrl = submitMessageBO.getCallBackUrl();
        final String content = submitMessageBO.getMessageBody();
        final Integer userId = submitMessageBO.getUserId();
        final String taskId = submitMessageBO.getTaskId();
        final String recipient = submitMessageBO.getRecipient();
        final String senderId = submitMessageBO.getSenderId();
        final Integer messageCount = submitMessageBO.getMessageCount();
        final String countryCode = submitMessageBO.getCountryCode();
        final String countryPrefix = submitMessageBO.getCountryPrefix();
        final BigDecimal costPrice = submitMessageBO.getCostPrice();
        final String agentId = submitMessageBO.getAgentId();
        final String nonce = UUID.randomUUID().toString();
        final String timestamp = DateHelp.localDateTime2Str(LocalDateTime.now());
        if (!CommonUtils.isMobile(recipient)) {
            throw new ServiceException("无效的手机号码.", UnionResultHelp.StatusEnum.ParamsFail.code());
        }

        try {
            final DomesticSmsRequestBodyBO bodyEntity = DomesticSmsRequestBodyBO.builder()
                    .access_key(apiKey)
                    .content(content)
                    .dest_number(recipient)
                    .nonce(nonce)
                    .signature(senderId)
                    .timestamp(timestamp)
                    .build();
            final String requestBody = new Gson().toJson(bodyEntity);
            String accessToken = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, apiSecret)
                    .hmacHex(requestBody);
            DomesticSmsResponseBO domesticSmsResponseBO = WebClient
                    .create(apiUrl.concat("/api/sms/submit"))
                    .post()
                    .accept(MediaType.APPLICATION_JSON)
                    .headers(httpHeaders -> {
                        httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
                        httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
                        httpHeaders.set("Access-Token", accessToken);
                    })
                    .body(BodyInserters.fromObject(requestBody))
                    .retrieve()
                    .onStatus(HttpStatus::is4xxClientError, clientResponse ->
                            Mono.error(new ServiceException("国内通道提交短信异常.", clientResponse.statusCode().value()))
                    )
                    .onStatus(HttpStatus::is5xxServerError, clientResponse ->
                            Mono.error(new ServiceException("国内通道提交短信异常.", clientResponse.statusCode().value()))
                    )
                    .bodyToMono(new ParameterizedTypeReference<DomesticSmsResponseBO>() {
                    })
                    .block();
            log.info("国内通道发送返回结果:{}", new Gson().toJson(domesticSmsResponseBO));
            String uuid = Objects.nonNull(domesticSmsResponseBO) ? domesticSmsResponseBO.getUuid() : "";
            Integer code = domesticSmsResponseBO.getCode();

            //根据返回的network查询当前号码所属国家的售价
            final String network = getNetwork(recipient);
            final SmsUserAllowOperator userAllowOperator = smsUserAllowOperatorService
                    .queryOneMaxPriceOrderByDesc(userId, countryCode, countryPrefix, network);
            if (Objects.isNull(userAllowOperator)) {
                throw new ServiceException("未同步运营商价格,或未找到:" + network + "服务通道.", UnionResultHelp.StatusEnum.InnerError.code());
            }
            final BigDecimal calcMessagePrice = MessageTypeHelp.calcMessagePrice(userAllowOperator.getPrice(), messageCount);
            final boolean uuIdNotEmpty = StringUtils.isNotEmpty(uuid);
            final SmsSubmissionRecord submissionRecord = SmsSubmissionRecord.builder()
                    .agentId(uuIdNotEmpty ? agentId : "")
                    .free(uuIdNotEmpty ? calcMessagePrice : BigDecimal.ZERO)
                    .costPrice(uuIdNotEmpty ? costPrice : BigDecimal.ZERO)
                    .prefix(countryPrefix)
                    .countryCode(countryCode)
                    .messageId(uuid)
                    .messageCount(messageCount)
                    .errorText(code == 0 ? "" : domesticSmsResponseBO.getMessage())
                    .network(network)
                    .submitStatusCode(code)
                    .finalStatus(uuIdNotEmpty ? StatusEum.SUBMITTED.desc : StatusEum.FAILED.desc)
                    .sendTo(recipient)
                    .taskId(taskId)
                    .userId(userId)
                    .minus(true)
                    .build();
            smsSubmissionRecordService.updateSmsSubmissionRecordFor(submissionRecord);
            if (uuIdNotEmpty) {
                //更新用户账户
                smsUserService.updateSmsPrice(userId, calcMessagePrice);
            }
            //异步查询短信状态
           // CompletableFuture.supplyAsync(() -> queryStatus(apiUrl, callBackUrl, apiKey, apiSecret, recipient, uuid, network));
            return SubmitMessageResultVO.builder()
                    .submitStatus(code)
                    .network(network)
                    .messageId(uuid)
                    .errorText(domesticSmsResponseBO.getMessage())
                    .messageCount(messageCount)
                    .build();

        } catch (Exception e) {
            log.error("国内发送短信异常:{}", e.getMessage(), e);
            throw new ServiceException("短信发送异常".concat(e.getMessage()), UnionResultHelp.StatusEnum.BadRequest.code());
        }
    }


    private String getNetwork(String recipient) {

        final String carrierEn = PhoneUtil.getCarrier(recipient.replaceAll("86", ""), 86);
        String networkCode = "";
        switch (carrierEn) {
            case "China Mobile":
                networkCode = "46000";
                break;
            case "China Unicom":
                networkCode = "46001";
                break;
            case "China Telecom":
                networkCode = "46003";
                break;
            default:
                break;
        }
        return networkCode;
    }

    private DomesticSmsQueryStatusResponseBodyBO queryStatus(String apiUrl, String callBackUrl, String apiKey,
                                                             String apiSecret, String recipient, String uuid, String network) {


        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            log.error(e.getMessage(), e);
        }
        log.info("查询状态消息ID:{},callBackUrl:{}", uuid, callBackUrl);
        final DomesticSmsQueryStatusRequestBodyBO queryStatusRequestBodyBO = DomesticSmsQueryStatusRequestBodyBO.builder()
                .access_key(apiKey)
                .timestamp(DateHelp.localDateTime2Str(LocalDateTime.now()))
                .nonce(UUID.randomUUID().toString())
                .uuid(uuid)
                .dest_number(recipient)
                .send_date(DateHelp.localDateTime2StrY_M_D(LocalDateTime.now()))
                .build();

        final String requestBody = new Gson().toJson(queryStatusRequestBodyBO);
        final String accessToken = new HmacUtils(HmacAlgorithms.HMAC_SHA_256, apiSecret).hmacHex(requestBody);

        final DomesticSmsQueryStatusResponseBodyBO responseBodyBO = WebClient
                .create(apiUrl.concat("/api/sms/report/acquire"))
                .post()
                .accept(MediaType.APPLICATION_JSON)
                .headers(httpHeaders -> {
                    httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
                    httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
                    httpHeaders.set("Access-Token", accessToken);
                })
                .body(BodyInserters.fromObject(requestBody))
                .retrieve()
                .onStatus(HttpStatus::is4xxClientError, clientResponse ->
                        Mono.error(new ServiceException("国内通道查询短信状态异常.", clientResponse.statusCode().value()))
                )
                .onStatus(HttpStatus::is5xxServerError, clientResponse ->
                        Mono.error(new ServiceException("国内通道查询短信状态异常.", clientResponse.statusCode().value()))
                ).bodyToMono(new ParameterizedTypeReference<DomesticSmsQueryStatusResponseBodyBO>() {
                })
                .block();
        log.info("查询状态为:{}", new Gson().toJson(responseBodyBO));
        boolean success = true;
        String errorCode = "0";
        String errorMessage = "";
        if (Objects.nonNull(responseBodyBO)) {
            final List<DomesticSmsQueryStatusResponseBodyBO.Detail> detail = responseBodyBO.getDetail();
            if (CollectionUtils.isNotEmpty(detail)) {
                success = detail.get(0).isSuccess();
                errorCode = responseBodyBO.getCode().toString();
                errorMessage = detail.get(0).getError_code();
            }
        }
        final DomesticSmsReportRequestBodyBO reportRequestBodyBO = DomesticSmsReportRequestBodyBO.builder()
                .timestamp(DateHelp.localDateTime2Str(LocalDateTime.now()))
                .nonce(UUID.randomUUID().toString())
                .report_time(DateHelp.localDateTime2Str(LocalDateTime.now()))
                .uuid(uuid)
                .dest_number(recipient)
                .sms_seq(1)
                .sms_size(1)
                .success(success)
                .error_code(errorCode)
                .error_message(errorMessage)
               // .network(network)
                .build();
        callBackReport(accessToken, callBackUrl, reportRequestBodyBO);
        return responseBodyBO;

    }

    private UnionResultHelp callBackReport(String accessToken, String callBackUrl, DomesticSmsReportRequestBodyBO reportRequestBodyBO) {
        log.info("推送报告：messageId:{},号码为:{}", reportRequestBodyBO.getUuid(), reportRequestBodyBO.getDest_number());
        WebClient
                .create(callBackUrl)
                .post()
                .accept(MediaType.APPLICATION_JSON)
                .headers(httpHeaders -> {
                    httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
                    httpHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
                    httpHeaders.set("Access-Token", accessToken);
                })
                .body(Mono.just(reportRequestBodyBO), DomesticSmsReportRequestBodyBO.class)
                .retrieve()
                .onStatus(HttpStatus::is4xxClientError, clientResponse ->
                        Mono.error(new ServiceException("推送报告异常.", clientResponse.statusCode().value()))
                )
                .onStatus(HttpStatus::is5xxServerError, clientResponse ->
                        Mono.error(new ServiceException("推送报告异常.", clientResponse.statusCode().value()))
                )
                .bodyToMono(new ParameterizedTypeReference<UnionResultHelp>() {
                })
                .block();
        return new UnionResultHelp(UnionResultHelp.StatusEnum.Success, "Success");
    }
}
